﻿using Common.Auth;
using Common.Caching;
using Common.Configurations;
using Common.Filters;
using Common.IoC;
using Common.Quartzs;
using FluentValidation;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Quartz;
using Quartz.Spi;
using System.Text;
using Microsoft.Extensions.Caching.Memory;
using LazyCache;
using Common.Utils;
using System.Text.Json.Serialization;
using StackExchange.Redis;


namespace Common.Extensions;

#nullable disable

/// <summary>
/// 依赖注入
/// </summary>
public static class DependencyInjectionExtensions
{
    /// <summary>
    /// 基础依赖注入服务
    /// </summary>
    /// <param name="services"></param>
    /// <param name="config"></param>
    /// <returns></returns>
    public static IServiceCollection AddDefaultServices(this IServiceCollection services,
       IConfiguration config)
    {
        #region 配置文件
        services.Configure<JwtSettings>(config.GetSection(JwtSettings.Key)); 
        services.Configure<CacheSettings>(config.GetSection(CacheSettings.Key));
        services.Configure<HostSettings>(config.GetSection(HostSettings.Key)); 
        services.Configure<MultiTenancySettings>(config.GetSection(MultiTenancySettings.Key));
        #endregion
        #region 缓存配置
        var cacheSettings = config.GetSection(CacheSettings.Key).Get<CacheSettings>();
        if (cacheSettings.CacheType.Equals("RedisCache", StringComparison.OrdinalIgnoreCase))
        {
            // Redis/Garnet
            services.AddSingleton<IConnectionMultiplexer>(provider =>
            {
                var configuration = ConfigurationOptions.Parse(cacheSettings.RedisConnectionString);
                return ConnectionMultiplexer.Connect(configuration);
            });
            services.AddSingleton<ICacheService, RedisCacheProvider>();
        }
        else
        {
            // MemoryCache
            services.AddSingleton<IAppCache>(provider => new CachingService());
            services.AddSingleton<ICacheService, AppCacheProvider>();
        }   
        #endregion
        #region IoC容器
        services.AddDependencyInjectionServices();
        #endregion
        #region Quartz 定时任务
        services.AddQuartz();
        services.AddSingleton<IJobFactory>(provider => new SingletonJobFactory(provider));
        #endregion
        #region 自动映射
        services.AddAutoMapper();
        #endregion
        #region 授权认证
        JwtSettings jwtSettings = config.GetSection("JwtSettings").Get<JwtSettings>()!;
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddAuthorization();
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401
            options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler);    //403
        }).AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new()
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = jwtSettings.Issuer,
                ValidAudience = jwtSettings.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecurityKey))
            };
            options.Events = new JwtBearerEvents
            {
                OnMessageReceived = context =>
                {
                    var accessToken = context.Request.Query["access_token"];
                    // If the request is for our hub...
                    var path = context.HttpContext.Request.Path;
                    if (!string.IsNullOrEmpty(accessToken) &&
                        (path.StartsWithSegments("/signalRHub")))
                    {
                        // Read the token out of the query string
                        context.Token = accessToken;
                    }
                    return Task.CompletedTask;
                }
            };
        }).AddScheme<AuthenticationSchemeOptions, ResponseAuthenticationHandler>(nameof(ResponseAuthenticationHandler), o => { });
        #endregion
        #region 控制器服务
        services.AddControllers().AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.Converters.Add(new LongConverter());//解决long类型数据精度丢失问题
            options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); // 枚举使用描述表示
            options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; // 忽略默认值属性
        });
        services.AddEndpointsApiExplorer();
        #endregion
        #region 拦截 FluentValidation 参数校验异常
        services.Configure<ApiBehaviorOptions>(options => //请求参数校验
        {
            options.InvalidModelStateResponseFactory = actionContext =>
            {
                var errors = actionContext.ModelState
                   .Where(e => e.Value?.Errors.Count > 0)
                   .Select(e => e.Value?.Errors.First().ErrorMessage)
                   .ToList();

                throw new ValidationException(errors?.Join("|"));
            };
        });
        #endregion
        #region 接口文档配置
        services.AddSwaggerGen(c =>
        {
            var swaggerSettings = config.GetSection(SwaggerSettings.Key).Get<SwaggerSettings>();
            c.SwaggerDoc("v1", new OpenApiInfo { Title = swaggerSettings.Title, Version = swaggerSettings.Version });
            c.AddServer(new OpenApiServer()
            {
                Url = swaggerSettings.Url,
                Description = swaggerSettings.Description
            });
            c.CustomOperationIds(apiDesc =>
            {
                var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
                return controllerAction!.ControllerName + "-" + controllerAction.ActionName;
            });
            foreach (var document in swaggerSettings.XmlDocuments)
            {
                c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{document}.xml"), true);
            }
            c.DocumentFilter<LowerCaseDocumentFilter>();

            // 添加授权定义
            c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = "JWT Authorization header using the Bearer scheme",
                Name = "Authorization",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.ApiKey
            });
            // 应用授权要求到操作上
            c.AddSecurityRequirement(new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                        }
                    },
                    new string[] {}
                }
            });
        });
        #endregion
        #region 响应压缩
        services.AddResponseCompression(options =>
        {
            options.EnableForHttps = true; // 启用 HTTPS 时也应用压缩
            options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
            {
                "text/plain",
                "text/html",
                "text/css",
                "application/javascript",
                "text/javascript",
                "application/json",
                "text/json",
                "application/xml",
                "text/xml",
                "image/svg+xml"
            });
        });
        #endregion

        return services;
    }
}
